﻿// -----------------------------------------------------------------------
// <copyright file="TablePermission.cs" company="JSC Centido">
//  Copyright (c) JSC Centido. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

using System.Data.Odbc;

namespace Centido.Core
{
    using System;
    using System.Diagnostics.CodeAnalysis;

    /// <summary>
    ///   Table access permission
    /// </summary>
    [SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix")]
    public sealed class TablePermission : IRemovable
    {
        /// <summary>
        ///   Access type
        /// </summary>
        private readonly DataAction _dataAction;

        /// <summary>
        ///   Principal
        /// </summary>
        private readonly string _principalName;

        /// <summary>
        ///   Table
        /// </summary>
        private readonly Table _table;

        /// <summary>
        ///   Public constructor
        /// </summary>
        /// <param name = "table">Table</param>
        /// <param name = "principalName">Principal name</param>
        /// <param name = "dataAction">Access type</param>
        internal TablePermission(Table table, string principalName, DataAction dataAction)
        {
            if (table == null)
                throw new ArgumentNullException("table");
            if (principalName == null)
                throw new ArgumentNullException("principalName");
            if (principalName.Trim().Length == 0)
                throw new ArgumentException("Principal name cannot be empty.");

            _table = table;
            _principalName = principalName.Trim().ToLower();
            _dataAction = dataAction;
        }

        /// <summary>
        ///   Table
        /// </summary>
        public Table Table
        {
            get { return _table; }
        }

        /// <summary>
        ///   Principal
        /// </summary>
        public IPrincipal Principal
        {
            get { return Domain.GetPrincipal(_principalName); }
        }

        /// <summary>
        ///   Domain
        /// </summary>
        public Domain Domain
        {
            get { return _table.Domain; }
        }

        /// <summary>
        ///   Access type
        /// </summary>
        public DataAction DataAction
        {
            get { return _dataAction; }
        }

        /// <summary>
        ///   Access level
        /// </summary>
        public AccessLevel AccessLevel
        {
            get
            {
                var accessLevel = AccessLevel.All;

                Domain.DbAccess.ExecuteScalar(
                    cmd =>
                    {
                        cmd.CommandText = string.Format(
                            "select {0} from {1} where {2}=? and {3}=? and {4}=?",
                            Db.Tables.TableAcl.Col.AccessLevel,
                            Domain.GetObjectFullName(Db.Tables.TableAcl.Name),
                            Db.Tables.TableAcl.Col.TableId,
                            Db.Tables.TableAcl.Col.PrincipalName,
                            Db.Tables.TableAcl.Col.DataAction);

                        cmd.Parameters.Add(
                            Domain.DbAccess.CreateParameter("table_id", OdbcType.Binary, _table.GetRawId()));
                        cmd.Parameters.Add(
                            Domain.DbAccess.CreateParameter("principal_name", OdbcType.NVarChar, _principalName));
                        cmd.Parameters.Add(
                            Domain.DbAccess.CreateParameter("access_type", OdbcType.Int, (int)_dataAction));
                    },
                    result =>
                    {
                        var accessLevelValue = DbValue.GetInteger(result).GetValueOrDefault();

                        accessLevel = (AccessLevel)accessLevelValue;
                    });

                return accessLevel;
            }
        }

        #region IRemovable Members

        /// <summary>
        ///   Delete this access rights
        /// </summary>
        public void Delete()
        {
            Domain.DbAccess.ExecuteNonQuery(
                cmd =>
                {
                    cmd.CommandText = string.Format("{{call {0}(?,?,?)}}",
                        Domain.GetObjectFullName(Db.Procedures.RevokeTablePermission));

                    cmd.Parameters.Add(Domain.DbAccess.CreateParameter("a_table_id",
                        OdbcType.Binary, Table.GetRawId()));
                    cmd.Parameters.Add(Domain.DbAccess.CreateParameter("a_principal_name",
                        OdbcType.NVarChar, Principal.Name));
                    cmd.Parameters.Add(Domain.DbAccess.CreateParameter("a_access_type",
                        OdbcType.Int, (int)DataAction));
                });
        }

        /// <summary>
        ///   Checks the existence of the table permission.
        /// </summary>
        /// <returns>Returns true if the table permission still exists; otherwise, returns false.</returns>
        public bool Exists()
        {
            var wasFound = false;

            Domain.DbAccess.ExecuteScalar(
                cmd =>
                {
                    cmd.CommandText = string.Format("select count(*) from {0} where {1}=? and {2}=? and {3}=?",
                        Domain.GetObjectFullName(Db.Tables.TableAcl.Name),
                        Db.Tables.TableAcl.Col.TableId,
                        Db.Tables.TableAcl.Col.PrincipalName,
                        Db.Tables.TableAcl.Col.DataAction);

                    cmd.Parameters.Add(Domain.DbAccess.CreateParameter("table_id", OdbcType.Binary, Table.GetRawId()));
                    cmd.Parameters.Add(Domain.DbAccess.CreateParameter("principal", OdbcType.NVarChar, Principal.Name));
                    cmd.Parameters.Add(Domain.DbAccess.CreateParameter("data_action", OdbcType.Int, DataAction));
                },
                result => wasFound = DbValue.GetInteger(result) != 0);

            return wasFound;
        }

        #endregion
    }
}